package muhtar.service;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import muhtar.common.MuhtarConstants;
import muhtar.util.FileUtil;

import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.ITopic;
import com.hazelcast.core.MessageListener;

import flexjson.JSONDeserializer;
import flexjson.JSONSerializer;

public class Muhtar implements MessageListener<String>
{
	private Map<String, String> configurationMap;
	private ITopic<String> topic;
	
	private JSONSerializer serializer;
	private JSONDeserializer<Map<String, String>> deserializer;	
	private File redoFile;
	
	private Set<String> adminCommands;
	
	public Muhtar() throws IOException {
		initialize();
	}
	
	private void initialize() throws IOException 
	{
		adminCommands = new HashSet<String>();
		adminCommands.add(MuhtarConstants.ADMIN_COMMAND_KEYS);
		adminCommands.add(MuhtarConstants.ADMIN_COMMAND_EDITOR);
		
		serializer = new JSONSerializer();
		deserializer = new JSONDeserializer<Map<String,String>>();
		
		configurationMap = Hazelcast.getMap(MuhtarConstants.MAP_NAME);
		topic = Hazelcast.getTopic(MuhtarConstants.TOPIC_NAME);
		topic.addMessageListener(this);
		
		if (configurationMap.size() == 0) {
			System.out.println("global cache empty, trying deserialize...");
			deserialize();
		}
	}
	
	private String showKeys() 
	{
		Set<String> keySet = new HashSet<String>();
		for (String key : configurationMap.keySet()) {
			keySet.add(key);
		}
		return serializer.serialize(keySet);
	}
	
	private String showEditor() {
		StringBuffer buffer = new StringBuffer();
		buffer.append("<html><head><title>muhtar</title></head><body><h3>Editor For Dummies</h3>");
		buffer.append("<form method='POST' action='/_system/editor'><table>");
		buffer.append("<tr><td><b>Key</b></td><td><input type='text' name='key'/></td></tr>");
		buffer.append("<tr><td><b>Value</b></td><td><input type='text' name='value'/></td></tr>");
		buffer.append("<tr><td></td><td><input type='submit'/></td></tr>");
		buffer.append("</form></table></body></html>");
		return buffer.toString();
	}
	
	private String executeAdminCommand(String command) 
	{
		if (MuhtarConstants.ADMIN_COMMAND_KEYS.equals(command)) {
			return showKeys();
		} else if (MuhtarConstants.ADMIN_COMMAND_EDITOR.equals(command)) {
			return showEditor();
		}
		return null;
	}
	
	public String get(String key) {
		if (adminCommands.contains(key)) {
			return executeAdminCommand(key);
		}
		return configurationMap.get(key);
	}
	
	public void set(String key, String value) throws IOException 
	{
		if (adminCommands.contains(key)) {
			throw new IOException("invalid key: " + key);
		}
		if (value == null) {
			configurationMap.remove(key);
		} else {
			configurationMap.put(key, value);	
		}
		topic.publish("serialize");
	}
	
	private synchronized File getRedoFile() 
	{
		if (redoFile == null) {
			redoFile = new File(FileUtil.getWorkingDirectory() + MuhtarConstants.REDO_FILE_NAME);
			System.out.println("using redo file: " + redoFile.getPath());
		}
		return redoFile;
	}
	
	private synchronized void serialize() throws IOException {
		String json = serializer.serialize(configurationMap);
		FileUtil.putBytesToFile(getRedoFile(), json.getBytes());
	}
	
	private void deserialize() throws IOException 
	{
		if (!getRedoFile().exists()) {
			System.out.println("no serialization file found");
			return;
		}
		String json = new String(FileUtil.getBytesFromFile(getRedoFile()));
		Map<String, String> map = deserializer.deserialize(json);
		for (String key : map.keySet()) {
			configurationMap.put(key, map.get(key));
			System.out.println(key + "=" + map.get(key));
		}
		System.out.println("cache deserialized");
	}

	public void onMessage(String message) 
	{
		try {
			serialize();
		} catch (IOException e) {
			System.out.println("error on serializing");
			e.printStackTrace();
		}
	}
}
